package model;

import java.awt.Image;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import model.Field.Directions;
import model.Game.Difficulties;
import utilities.AbstractGraph.Edge;
import utilities.AbstractGraph.Tree;
import utilities.UnweightedGraph;

/**
 *
 * @author Misja
 */
public class Enemy extends Part {

    private ArrayList<Edge> edges;
    private ArrayList<Field> vertices;
    private HashMap<Field, Boolean> processed;
    private int playerIndex;
    private int enemyIndex;   

    public Enemy(Image appereance) {
        super(appereance);
        super.setMovable(false);
        this.edges = new ArrayList<>();
        this.vertices = new ArrayList<>();
        this.processed = new HashMap<>();
        this.enemyIndex = 0;
        this.playerIndex = 0;
    }

    /*
     * Scanned het veld
     * 
     */
    private void scan() {
        // leegmaken van de vertices edges en processed list en de index van de enemy bepalen
        this.edges.clear();
        this.vertices.clear();
        this.processed.clear();

        // veld van de enemy op in de processed lijst zetten
        this.processed.put(super.currentField, false);
        boolean endCondition = false;

        while (!endCondition) {
            Field current = null;
            // bepalen van het eerste false veld of de eind conditie
            Iterator<Map.Entry<Field, Boolean>> it = this.processed.entrySet().iterator();
            while (it.hasNext()) {
                Entry<Field, Boolean> entry = it.next();
                // als er een false processed is dan hebben een een current
                if (!entry.getValue()) {
                    current = entry.getKey();
                    if (!vertices.contains(current)) {
                        vertices.add(current);
                    }
                    break;
                }
            }
            // kijkt of er al aan de
            if (current != null) {
                
                setVerticeEdge(current, Directions.RIGHT);
                setVerticeEdge(current, Directions.LEFT);
                setVerticeEdge(current, Directions.UP);
                setVerticeEdge(current, Directions.DOWN);

                processed.remove(current);
                processed.put(current, true);
            } else {
                endCondition = true;
            }
        }
    }

    private void setVerticeEdge(Field current, Directions direction) {
        if (current.getNeighbor(direction) != null) {
            Field neighbor = current.getNeighbor(direction);
            // alleen als er geen Part zit in een veld of als er een speler inzit wordt hij meegenomen in de graaf.
            if (neighbor.getPart() == null || neighbor.getPart() instanceof Player) {
                if (!processed.containsKey(neighbor)) {
                    processed.put(neighbor, false);
                    if (!vertices.contains(neighbor)) {
                        vertices.add(neighbor);
                        if (neighbor.getPart() instanceof Player) {
                            this.playerIndex = vertices.indexOf(neighbor);
                        }
                    }
                }
                // voor het opbouwen van een adjencency matrix zijn biede richtingen van de edges nodig
                edges.add(new Edge(vertices.indexOf(current), vertices.indexOf(neighbor)));
                edges.add(new Edge(vertices.indexOf(neighbor), vertices.indexOf(current)));
            }
        }
    }

    public AfterMoveConditions move(Difficulties difficultie) {
        AfterMoveConditions condition = null;
        switch (difficultie) {
            case EASY:
                condition = this.moveEasy();
                break;
            case MEDIUM:
                condition = this.moveMedium();
                break;
            case HARD:
                condition = this.moveMedium();
                break;
        }
        return condition;
    }

    private AfterMoveConditions moveEasy() {
        Random random = new Random();
        ArrayList<Field> validOptions = new ArrayList<>();

        for (Directions direction : Directions.values()) {
            Field neighborField = this.currentField.getNeighbor(direction);

            if (neighborField != null) {
                if (neighborField.getPart() instanceof Player) {
                    currentField.setPart(null);
                    neighborField.setPart(this);
                    return AfterMoveConditions.LASTMOVE;
                } else if (neighborField.getPart() == null) {
                    validOptions.add(neighborField);
                }
            }
        }

        if (!validOptions.isEmpty()) {
            int x = random.nextInt((validOptions.size()));
            Field neighbor = validOptions.get(x);
            currentField.setPart(null);
            neighbor.setPart(this);
            return AfterMoveConditions.MOVED;
        }

        return AfterMoveConditions.NOTMOVED;
    }

    private AfterMoveConditions moveMedium() {
        this.scan();
        UnweightedGraph<Field> graph = new UnweightedGraph<>(this.edges, this.vertices);
        Tree bfs = graph.bfs(enemyIndex);
       // validatie of de speler wel kan voorkomen in het path he is een path van edges als de speler index niet kleiner is dan het aantal vertices dan gaat het fout
        if (playerIndex < bfs.getNumberOfVerticesFound()) {
            List path = bfs.getPath(playerIndex);


            boolean validPath = false;
            Iterator<Field> it = path.iterator();
            while (it.hasNext()) {
                Field f = it.next();
                if (f.getPart() instanceof Player) {
                    validPath = true;
                    break;
                }
            }

            if (validPath) {
                currentField.setPart(null);
                Field nextField = (Field) path.get(path.size() - 2);
                if (nextField.getPart() instanceof Player) {
                    nextField.setPart(this);
                    return AfterMoveConditions.LASTMOVE;
                } else {
                    nextField.setPart(this);
                    return AfterMoveConditions.MOVED;
                }
            } else {
                return this.moveEasy();
            }
        } else {
            return this.moveEasy();
        }
    }

}
